﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace LibSnesDotNet.Video
{
	public class DwmApi
	{
		[DllImport("dwmapi.dll")]
		static extern IntPtr DwmIsCompositionEnabled(out bool isEnabled);
		[DllImport("dwmapi.dll")]
		static extern IntPtr DwmSetPresentParameters(IntPtr hwnd, ref DwmPresentParameters parameters);
		[DllImport("dwmapi.dll")]
		static extern IntPtr DwmEnableMMCSS(bool shouldEnable);
		[DllImport("dwmapi.dll")]
		static extern IntPtr DwmGetCompositionTimingInfo(IntPtr hwnd, ref DwmTimingInfo timingInfo);
		[DllImport("kernel32.dll")]
		static extern bool QueryPerformanceCounter(out long pc);
		[DllImport("kernel32.dll")]
		static extern bool QueryPerformanceFrequency(out long freq);

		public static bool IsCompositionEnabled
		{
			get
			{
				bool isEnabled;
				try
				{
					DwmIsCompositionEnabled(out isEnabled);
				}
				catch
				{
					isEnabled = false;
				}

				return isEnabled;
			}
		}

		public static long PerformanceCounter
		{
			get
			{
				long pc;
				if (!QueryPerformanceCounter(out pc)) { throw new NotSupportedException(); }
				return pc;
			}
		}

		public static long PerformanceFrequency
		{
			get
			{
				long freq;
				if (!QueryPerformanceFrequency(out freq)) { throw new NotSupportedException(); }
				return freq;
			}
		}

		public static void SetPresentParameters(IntPtr hwnd, ref DwmPresentParameters parameters)
		{
			parameters.Size = (uint)Marshal.SizeOf(parameters);

			Marshal.ThrowExceptionForHR((int)DwmSetPresentParameters(hwnd, ref parameters));
		}

		public static void EnableMMCSS(bool shouldEnable)
		{
			DwmEnableMMCSS(shouldEnable);
		}

		public static DwmTimingInfo GetCompositionTimingInfo(IntPtr hwnd)
		{
			DwmTimingInfo timingInfo = new DwmTimingInfo();
			timingInfo.Size = (uint)Marshal.SizeOf(timingInfo);
			Marshal.ThrowExceptionForHR((int)DwmGetCompositionTimingInfo(hwnd, ref timingInfo));
			return timingInfo;
		}
	}

	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct DwmPresentParameters
	{
		internal uint Size;
		public bool IsQueued;
		public ulong RefreshStart;
		public uint BufferCount;
		public bool UseSourceRate;
		public UnsignedRatio SourceRate;
		public uint RefreshesPerFrame;
		public DwmFrameSampling Sampling;
	}

	[StructLayout(LayoutKind.Sequential, Pack = 1)]
	public struct DwmTimingInfo
	{
		internal uint Size;
		public UnsignedRatio RateRefresh;
		public ulong QpcRefreshPeriod;
		public UnsignedRatio RateCompose;
		public ulong QpcVBlank;
		public ulong Refresh;
		public uint DXRefresh;
		public ulong QpcCompose;
		public ulong Frame;
		public uint DXPresent;
		public ulong RefreshFrame;
		public ulong FrameSubmitted;
		public uint DXPresentSubmitted;
		public ulong FrameConfirmed;
		public uint DXPresentConfirmed;
		public ulong RefreshConfirmed;
		public uint DXRefreshConfirmed;
		public ulong FramesLate;
		public uint FramesOutstanding;
		public ulong FrameDisplayed;
		public ulong QpcFrameDisplayed;
		public ulong RefreshFrameDisplayed;
		public ulong FrameComplete;
		public ulong QpcFrameComplete;
		public ulong FramePending;
		public ulong QpcFramePending;
		public ulong FramesDisplayed;
		public ulong FramesComplete;
		public ulong FramesPending;
		public ulong FramesAvailable;
		public ulong FramesDropped;
		public ulong FramesMissed;
		public ulong RefreshNextDisplayed;
		public ulong RefreshNextPresented;
		public ulong RefreshesDisplayed;
		public ulong RefreshesPresented;
		public ulong RefreshStarted;
		public ulong PixelsReceived;
		public ulong PixelsDrawn;
		public ulong BuffersEmpty;
	}

	[StructLayout(LayoutKind.Sequential)]
	public struct UnsignedRatio
	{
		public uint Numerator;
		public uint Denominator;

		public UnsignedRatio(uint numerator, uint denominator)
		{
			Numerator = numerator;
			Denominator = denominator;
		}

		public override string ToString()
		{
			return Numerator.ToString() + '/' + Denominator.ToString();
		}
	}

	public enum DwmFrameSampling
	{
		Default = 0,
		Point = 1,
		Coverage
	}
}
